package com.funny.stock.trade.service.monitor;

import com.funny.stock.base.redis.RedisTool;
import com.funny.stock.base.util.ResultUtil;
import com.funny.stock.base.vo.Result;
import com.funny.stock.base.vo.TradeType;
import com.funny.stock.data.collect.StockMonitorStrategy;
import com.funny.stock.data.collect.bean.StockMonitorContext;
import com.funny.stock.data.vo.RealtimeStockData;
import com.funny.stock.data.vo.StockConstants;
import com.funny.stock.trade.dao.pojo.DayTradePlanVarDO;
import com.funny.stock.trade.dao.pojo.DayTradeProfitDO;
import com.funny.stock.trade.service.ComposeStockActionService;
import com.funny.stock.trade.service.DayTradePlanService;
import com.funny.stock.trade.service.DayTradePlanVarService;
import com.funny.stock.trade.service.StockMsgNotifyService;
import com.funny.stock.trade.util.PriceUtils;
import com.funny.stock.trade.vo.DayTradePlanVO;
import com.funny.stock.trade.vo.MonitorActionInfo;
import com.funny.stock.trade.vo.TradeActionInfo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * T0监控服务
 *
 * @author pengl
 * @date 2020/11/11
 */
@Component
public class T0StockMonitorService implements StockMonitorStrategy {
    private static final Logger logger = LoggerFactory.getLogger(T0StockMonitorService.class);

//    private Lock lock = new ReentrantLock();

    @Autowired
    DayTradePlanService dayTradePlanService;

    @Autowired
    RedisTool redisTool;

//    @Autowired
//    MailService mailService;

    @Autowired
    StockMsgNotifyService stockMsgNotifyService;

    @Autowired
    TaskExecutor taskExecutor;

//    @Autowired
//    DayTradeRecordService dayTradeRecordService;
    @Autowired
    ComposeStockActionService actionService;

    @Autowired
    DayTradePlanVarService dayTradePlanVarService;

    @Override
    public void onMonitor(StockMonitorContext context) {
        List<DayTradePlanVO> plans = dayTradePlanService.queryTradingPlanList();

//        logger.info("执行日内TO交易计划的数目:{}",plans.size());
        if(plans.size() == 0) return;

//        try {
//            if(lock.tryLock(StockConstants.LOCK_TYR_SECONDS,TimeUnit.SECONDS)){
                //如果单次执行计划时长超过更新数据时长，存在问题，待解决
                StopWatch stopWatch = new StopWatch("t0Plan");
                stopWatch.start();

                Map<String, RealtimeStockData> dataMap = context.getDataMap();
                for (DayTradePlanVO plan : plans) {
                    RealtimeStockData realtimeStockData = dataMap.get(plan.getCode());
                    if(realtimeStockData == null) continue;

                    invokePlan(realtimeStockData, plan);
                }

                stopWatch.stop();

                if(stopWatch.getTotalTimeMillis() >= StockConstants.DATA_REFRESH_INTERVAL_SECONDS * 500){
                    logger.warn("执行日内TO交易消耗时长/ms:{}",stopWatch.getTotalTimeMillis());
                }
                logger.info("执行日内TO交易计划的数目:{},耗时/ms:{}",plans.size(),stopWatch.getTotalTimeMillis());
//            }else {
//                logger.info("获取执行T0的计划锁失败");
//            }
//        } catch (InterruptedException e) {
////            e.printStackTrace();
//            logger.error("获取执行T0的计划锁异常:",e);
//        }finally {
//            lock.unlock();
//        }
    }

    private void invokePlan(RealtimeStockData realtimeStockData,DayTradePlanVO planDO){
//        logger.info("开始执行TO计划:id:{},code:{}", planDO.getId(),planDO.getCode());

        String monitorKey = StockConstants.KEY_T0_MONITOR_PREFIX + planDO.getCode() + "-" + planDO.getId();
        String tradeKey = StockConstants.KEY_T0_TRADE_PREFIX + planDO.getCode() + "-" + planDO.getId();

        String lastMonitorPer = (String) getHashValue(monitorKey,  StockConstants.KEY_T0_LAST_MONITOR_PER);
        String lastTradePer = (String) getHashValue(tradeKey,  StockConstants.KEY_T0_LAST_TRADE_PER);

        BigDecimal curMonitorIncreasePer = planDO.getMonitorIncreasePer();
        BigDecimal curMonitorDecreasePer = planDO.getMonitorDecreasePer();
        if(!StringUtils.isEmpty(lastMonitorPer)){
            //当天有涨跌幅触发监控
            BigDecimal monitorPer = new BigDecimal(lastMonitorPer);
            curMonitorIncreasePer = monitorPer.add(curMonitorIncreasePer);
            curMonitorDecreasePer = monitorPer.add(curMonitorDecreasePer);
        }

        BigDecimal curTradeIncreasePer = planDO.getTradeIncreasePer();
        BigDecimal curTradeDecreasePer = planDO.getTradeDecreasePer();
        if(!StringUtils.isEmpty(lastTradePer)){
            //当天有涨跌幅触发交易
            BigDecimal monitorPer = new BigDecimal(lastTradePer);
            curTradeIncreasePer = monitorPer.add(curTradeIncreasePer);
            curTradeDecreasePer = monitorPer.add(curTradeDecreasePer);
        }

        //通知触发条件  涨幅，跌幅，交易委托条件  涨幅，跌幅  数量
        //判断当天是否触发过监控通知，进行过交易
        //获取下次涨幅或跌幅监控触发的比例，获取下次交易触发的的比例

        BigDecimal realPercent = BigDecimal.valueOf(realtimeStockData.getPercent())
                .multiply(new BigDecimal(100))
                .setScale(2,BigDecimal.ROUND_HALF_UP);
//        logger.info("code:{},percent:{},price:{},arrow:{}",planDO.getCode(),realPercent.toString(),realtimeStockData.getPrice(),realtimeStockData.getArrow());
        logger.info("T0监控:{},下次预计买入涨幅:{},卖出跌幅:{},当前幅度:{}",realtimeStockData.getSymbol(),curMonitorDecreasePer,curMonitorIncreasePer,realPercent);

        boolean isMonitorUp = realPercent.compareTo(curMonitorIncreasePer) >= 0;
        if(isMonitorUp || realPercent.compareTo(curMonitorDecreasePer) <= 0){
            logger.info("触发监控通知:{}",curMonitorIncreasePer);
            //触发监控涨幅或跌幅，发送邮件，设置下一次监控触发条件

            putHashValue(monitorKey, StockConstants.KEY_T0_LAST_MONITOR_PER, realPercent.toString());
            putHashValue(monitorKey, StockConstants.KEY_T0_LAST_MONITOR_PRICE, String.valueOf(realtimeStockData.getPrice()));
            putHashValue(monitorKey, StockConstants.KEY_T0_LAST_MONITOR_TIME, String.valueOf(System.currentTimeMillis()));
            expireTime(monitorKey, StockConstants.STOCK_T0_EXPIRE_HOURS, TimeUnit.HOURS);

            //提醒
//            BigDecimal lastPer = BigDecimal.ZERO;
//            if(lastMonitorPer != null){
//                lastPer = new BigDecimal(lastMonitorPer);
//            }

            doMonitorAction(realtimeStockData, planDO, realPercent, isMonitorUp);
        }

        boolean isTradeUp = realPercent.compareTo(curTradeIncreasePer) >= 0;
//        logger.info("T0交易计划:{},下次预计买入涨幅:{},卖出跌幅:{},当前幅度:{}",realtimeStockData.getSymbol(),curTradeDecreasePer,curTradeIncreasePer,realPercent);

        if(isTradeUp || realPercent.compareTo(curTradeDecreasePer) <= 0){
            logger.info("触发交易通知:{}",curTradeIncreasePer);

            //触发交易涨幅或跌幅，发送邮件，设置下一次监控触发条件
            String basePrice = String.valueOf(realtimeStockData.getPrice());
            putHashValue(tradeKey, StockConstants.KEY_T0_LAST_TRADE_PER, realPercent.toString());
            putHashValue(tradeKey, StockConstants.KEY_T0_LAST_TRADE_PRICE, basePrice);
            putHashValue(tradeKey, StockConstants.KEY_T0_LAST_TRADE_TIME, String.valueOf(System.currentTimeMillis()));
            expireTime(tradeKey, StockConstants.STOCK_T0_EXPIRE_HOURS, TimeUnit.HOURS);

            //提醒
//            BigDecimal lastPer = BigDecimal.ZERO;
//            if(lastMonitorPer != null){
//                lastPer = new BigDecimal(lastTradePer);
//            }
            DayTradePlanVarDO tempPlanVar = new DayTradePlanVarDO();
            List<BigDecimal> priceList = PriceUtils.calcNextTradePriceList(new BigDecimal(basePrice),
                    planDO.getTradeIncreasePer(), planDO.getTradeDecreasePer());

            tempPlanVar.setNextBuyPrice(priceList.get(0));
            tempPlanVar.setNextSellPrice(priceList.get(1));
            tempPlanVar.setPlanId(planDO.getId());

            //执行买入卖出操作记录,发送邮件提醒
            doTradeAction(realtimeStockData, planDO, tempPlanVar, isTradeUp);
        }
    }

    public void doTradeAction(RealtimeStockData realtimeStockData, DayTradePlanVO planDO, DayTradePlanVarDO tempPlanVar,boolean isUp) {

        TradeActionInfo actionInfo = buildTradeActionInfo(realtimeStockData, planDO, StockConstants.STOCK_STRATEGY_T0, isUp ? TradeType.SELL : TradeType.BUY);

        taskExecutor.execute(()-> {
            if(tempPlanVar != null)
                dayTradePlanVarService.savePlan(tempPlanVar);

            Result<DayTradeProfitDO> result = actionService.doTradeActionByMonitor(actionInfo);
            if(result.getCode() == ResultUtil.CODE_SUCCESS) {
                DayTradeProfitDO data = result.getData();
                if(data != null)
                    actionInfo.setMatchProfit(data.getProfit());

                stockMsgNotifyService.onTradeNotify(actionInfo);
            }else{
                stockMsgNotifyService.onTradeNotifyFailMsg(actionInfo, result.getCode(), result.getMsg());
            }
        });
    }

    public static TradeActionInfo buildTradeActionInfo(RealtimeStockData realtimeStockData, DayTradePlanVO planDO,
                                                       String planType, TradeType tradeType){
        TradeActionInfo actionInfo = new TradeActionInfo();
        actionInfo.setPersonId(planDO.getCreateId());
        actionInfo.setPlanId(planDO.getId());
        actionInfo.setComposeId(planDO.getComposeId());
        actionInfo.setPlanType(planType);
        actionInfo.setCode(planDO.getCode());
        actionInfo.setRealtimeStockData(realtimeStockData);
        actionInfo.setTradeNumber(planDO.getTradeStockNum());
        actionInfo.setTradeType(tradeType);

        actionInfo.setPlanIncreasePer(planDO.getTradeIncreasePer() == null ? "" : planDO.getTradeIncreasePer().toString());
        actionInfo.setPlanDecreasePer(planDO.getTradeDecreasePer() == null ? "" : planDO.getTradeDecreasePer().toString());

        return actionInfo;
    }

    public static MonitorActionInfo buildMonitorActionInfo(RealtimeStockData realtimeStockData, DayTradePlanVO planDO,
                                                         String planType, TradeType tradeType){
        MonitorActionInfo actionInfo = new MonitorActionInfo();
        actionInfo.setPersonId(planDO.getCreateId());
        actionInfo.setComposeId(planDO.getComposeId());
        actionInfo.setPlanId(planDO.getId());
        actionInfo.setPlanType(planType);
        actionInfo.setCode(planDO.getCode());
        actionInfo.setRealtimeStockData(realtimeStockData);
        actionInfo.setTradeType(tradeType);

        actionInfo.setPlanIncreasePer(planDO.getMonitorIncreasePer() == null ? "" : planDO.getMonitorIncreasePer().toString());
        actionInfo.setPlanDecreasePer(planDO.getMonitorDecreasePer() == null ? "" : planDO.getMonitorDecreasePer().toString());

        return actionInfo;
    }

    private void doMonitorAction(RealtimeStockData realtimeStockData, DayTradePlanVO planDO,
                                 BigDecimal realPercent, boolean isUp) {
        MonitorActionInfo actionInfo = buildMonitorActionInfo(realtimeStockData, planDO, StockConstants.STOCK_STRATEGY_T0, isUp ? TradeType.SELL : TradeType.BUY);

        taskExecutor.execute(()-> {
            stockMsgNotifyService.onMonitorNotify(actionInfo);
        });
    }


    private Object getHashValue(String oKey,String hKey){
        return redisTool.hGet(oKey, hKey);
    }

    private void putHashValue(String oKey,String hKey,String value){
        redisTool.hPut(oKey, hKey, value);
        expireTime(oKey, StockConstants.KEY_DEFAULT_EXPIRE_HOURS, TimeUnit.HOURS);
    }

    private void expireTime(String key, long timeout, TimeUnit timeUnit){
        redisTool.expire(key, timeout, timeUnit);
    }

}
